package midend.IR_Optimizer;

import midend.IR_Instruction.*;
import midend.IR_Type.IR_Int32;
import midend.IR_Type.IR_Float;
import midend.IR_Type.IR_Pointer;
import midend.IR_Value.*;

import java.util.*;
/**
 * 公共子表达式消除
 * int a = b + c;
 * int d = b + c + 1;
 * // 优化后
 * int a = b + c;
 * int d = a + 1;
 */

public class Common_Subexpression_Eli implements IR_Opt {
    @Override
    public void Optimize(IR_FuncBlock functionBlock) {
        // 遍历函数块中的所有基本块
        for (IR_BaseBlock bb : functionBlock.get_bb_List()) {
            List<Instruction> instructions = bb.get_Instruction_List();

            // key记录可以被替换的变量，value记录替换值
            Map<IR_Value,IR_Value> common_Sub_Exp = new HashMap<>();

            // 用于记录每个calculate指令的操作数，以及对应的指令
            Map<List<IR_Value>,Calc_Instruction> common_Sub_Calc = new HashMap<>();

            // 用于记录每个getElementPointer指令的操作数，以及对应的指令
            Map<IR_Value,Pointer_Instruction> common_Sub_Pointer = new HashMap<>();

            // 用于记录每个Compare指令的操作数，以及对应的指令
            Map<List<IR_Value>,Cond_Instruction> common_Sub_Compare = new HashMap<>();

            // 用于记录每个Phi指令的操作数，以及对应的指令
            Map<Map<IR_BaseBlock,IR_Value>,Phi_Instruction> common_Sub_Phi = new HashMap<>();

            // 用于记录每个TypeTransfer指令的操作数，以及对应的指令
            Map<List<IR_Value>,Type_Trans_Instruction> common_SubType_Transfer = new HashMap<>();

            // 用于记录每个Zext指令的操作数，以及对应的指令
            Map<List<IR_Value>,Zext_Instruction> common_Sub_Zext = new HashMap<>();

            // 遍历每条指令
            for (int i = 0;i < instructions.size();i++) {
                Instruction instruction = instructions.get(i);
                //首先查看该指令的所有操作数是否在commonSubExp中，如果在，则将其替换为commonSubExp中的值
                for (int j = 0; j < instruction.get_op().size(); j++) {
                    IR_Value instr = instruction.get_op().get(j);
                    if(common_Sub_Exp.containsKey(instr)){
                        instruction.set_Op(j,common_Sub_Exp.get(instr));
                    }
                }

                // 检查Pointer_Instruction中的特定参数
                if(instruction instanceof Pointer_Instruction){
                    // 这个地方是否需要再次检查呢？毕竟前面已经多ops进行了检查，这里的base是否在ops里面呢
                    if(common_Sub_Exp.containsKey(((Pointer_Instruction) instruction).get_base())){
                        ((Pointer_Instruction)instruction).set_Base(common_Sub_Exp.get(((Pointer_Instruction) instruction).get_base()));
                    }
                    for(int k = 0;k < ((Pointer_Instruction) instruction).get_list().size();k++){
                        if(common_Sub_Exp.containsKey(((Pointer_Instruction) instruction).get_list().get(k))){
                            ((Pointer_Instruction)instruction).set_List(k,common_Sub_Exp.get(((Pointer_Instruction) instruction).get_list().get(k)));
                        }
                    }
                }

                //检查call指令中的特定参数
                if(instruction instanceof Call_Instruction){
                    for(int k = 0;k < ((Call_Instruction)instruction).get_params().size();k++){
                        if(common_Sub_Exp.containsKey(((Call_Instruction)instruction).get_params().get(k))){
                            ((Call_Instruction)instruction).setParams(k,common_Sub_Exp.get(((Call_Instruction)instruction).get_params().get(k)));
                        }
                    }
                }

                // 其次检查每条指令能否进行公共子表达删除
                if (instruction instanceof Calc_Instruction) {
                    // 每条源操作数（除目的操作数之外）不同的cal指令第一次出现时，记录该指令的源操作数到commonSubCalc中（包括运算符）
                    //如果遇到后面有的cal指令源操作数和commonSubCalc中的某个key一样，说明可以进行公共子表达式替换，
                    // 将（res,res_oringin）加入到commonSubExp中，并删除这条calc指令
                    //可以在res引用处用res_oringin进行替换
                    List<IR_Value> ops = instruction.get_op();
                    IR_Value resRegister = ops.get(0);
                    ops = ops.subList(1,ops.size());  // 从操作数列表中移除第一个元素，创建一个新的子列表
                    if(common_Sub_Calc.containsKey(ops)
                            && common_Sub_Calc.get(ops).get_op_Type().equals(((Calc_Instruction) instruction).get_op_Type())){ //
                        //如果计算的类型以及操作数相同
                        IR_Value temp = common_Sub_Calc.get(ops).get_op().get(0);
                        //将这一条指令删除
                        instruction.get_bb().get_Instruction_List().remove(instruction);
                        i --;
                        //将这一条指令的左值记录在commonSubExp中
                        common_Sub_Exp.put(resRegister,temp);  // 被替代值是key，替代值是value
                    }else{
                        common_Sub_Calc.put(ops,(Calc_Instruction) instruction); //
                    }
                }
                else if (instruction instanceof Pointer_Instruction) {
                    List<IR_Value> ops = instruction.get_op();
                    IR_Value resRegister = ops.get(0);
                    //获取GetElementPointer指令的操作数
                    //如果两个操作数都是int类型的常数
                    if(ops.size() == 4) {
                        if (!(ops.get(2) instanceof IR_Int_Value) || !(ops.get(3) instanceof IR_Int_Value)) {
                            //如果不是int类型的常数，那么直接比较即可
                            if (common_Sub_Pointer.containsKey(ops.get(1))
                                    && common_Sub_Pointer.get(ops.get(1)).get_op().get(2).equals(ops.get(2))
                                    && common_Sub_Pointer.get(ops.get(1)).get_op().get(3).equals(ops.get(3))) {
                                IR_Value temp = common_Sub_Pointer.get(ops.get(1)).get_op().get(0);
                                instruction.get_bb().get_Instruction_List().remove(instruction);
                                i--;
                                //将这一条指令的左值记录在commonSubExp中
                                common_Sub_Exp.put(resRegister, temp);
                            } else {
                                common_Sub_Pointer.put(ops.get(1), (Pointer_Instruction) instruction);
                            }
                        } else {
                            //如果两个操作数都是int类型的常数
                            int i1 = ((IR_Int_Value) (ops.get(2))).get_Int_Value();
                            int i2 = ((IR_Int_Value) (ops.get(3))).get_Int_Value();
                            if (common_Sub_Pointer.containsKey(ops.get(1))
                                    && (common_Sub_Pointer.get(ops.get(1)).get_op().get(2)) instanceof IR_Int_Value
                                    && (common_Sub_Pointer.get(ops.get(1)).get_op().get(3)) instanceof IR_Int_Value
                                    && i1 == ((IR_Int_Value) (common_Sub_Pointer.get(ops.get(1)).get_op().get(2))).get_Int_Value()
                                    && i2 == ((IR_Int_Value) (common_Sub_Pointer.get(ops.get(1)).get_op().get(3))).get_Int_Value()
                            ) {
//                                if(((GetElementPointerInstruction) instruction).getBase().getType() instanceof IRPointerType
//                                        && ((IRPointerType)((GetElementPointerInstruction) instruction).getBase().getType()).getBaseType() instanceof IRArrayType
//                                        &&((IRArrayType)((IRPointerType)((GetElementPointerInstruction) instruction).getBase().getType()).getBaseType()).getBaseType() instanceof IRFloatType) {
//                                    //如果是float类型的数组，那么不能继续优化
//                                    continue;
//                                }
                                //如果计算的类型以及操作数相同
                                IR_Value temp = common_Sub_Pointer.get(ops.get(1)).get_op().get(0);
                                //将这一条指令删除
                                instruction.get_bb().get_Instruction_List().remove(instruction);
                                i--;
                                //将这一条指令的左值记录在commonSubExp中
                                common_Sub_Exp.put(resRegister, temp);
                            } else {
                                common_Sub_Pointer.put(ops.get(1), (Pointer_Instruction) instruction);
                            }
                        }
                    } else{  // ops.size()=3
                        if (!(ops.get(2) instanceof IR_Int_Value)){
                            if(common_Sub_Pointer.containsKey(ops.get(1))
                                    && common_Sub_Pointer.get(ops.get(1)).get_op().get(2).equals(ops.get(2))) {
                                //如果计算的类型以及操作数相同
                                IR_Value temp = common_Sub_Pointer.get(ops.get(1)).get_op().get(0);
                                //将这一条指令删除
                                instruction.get_bb().get_Instruction_List().remove(instruction);
                                i--;
                                //将这一条指令的左值记录在commonSubExp中
                                common_Sub_Exp.put(resRegister, temp);
                            }else{
                                common_Sub_Pointer.put(ops.get(1), (Pointer_Instruction) instruction);
                            }
                        }else{
                            int temp = ((IR_Int_Value) (ops.get(2))).get_Int_Value();
                            if(common_Sub_Pointer.containsKey(ops.get(1))
                                    && temp == ((IR_Int_Value) (common_Sub_Pointer.get(ops.get(1)).get_op().get(2))).get_Int_Value()) {
                                //如果计算的类型以及操作数相同
                                IR_Value temp1 = common_Sub_Pointer.get(ops.get(1)).get_op().get(0);
                                //将这一条指令删除
                                instruction.get_bb().get_Instruction_List().remove(instruction);
                                i--;
                                //将这一条指令的左值记录在commonSubExp中
                                common_Sub_Exp.put(resRegister, temp1);
                            }else{
                                common_Sub_Pointer.put(ops.get(1), (Pointer_Instruction) instruction);
                            }
                        }
                    }
                }
                else if (instruction instanceof Cond_Instruction) {
                    List<IR_Value> ops = instruction.get_op();
                    IR_Value resRegister = ops.get(0);
                    ops = ops.subList(1,ops.size());
                    // compareType是一个整数索引，用来指示compare的类型，如eq, nq, ugt
                    if(common_Sub_Compare.containsKey(ops) && common_Sub_Compare.get(ops).get_cond() == ((Cond_Instruction) instruction).get_cond()){
                        //如果计算的类型以及操作数相同
                        IR_Value temp = common_Sub_Compare.get(ops).get_op().get(0);
                        //将这一条指令删除
                        instruction.get_bb().get_Instruction_List().remove(instruction);
                        // i --;
                        //将这一条指令的左值记录在commonSubExp中
                        common_Sub_Exp.put(resRegister,temp);
                    }else{
                        common_Sub_Compare.put(ops,(Cond_Instruction) instruction);
                    }
                }
                else if (instruction instanceof Phi_Instruction) {
                    Map<IR_BaseBlock,IR_Value> ops = ((Phi_Instruction) instruction).get_ComingValues();
                    IR_Value resRegister = instruction.get_op().get(0);
                    if(common_Sub_Phi.containsKey(ops)){
                        //如果计算的类型以及操作数相同
                        IR_Value temp = common_Sub_Phi.get(ops).get_op().get(0);
                        //将这一条指令删除
                        instruction.get_bb().get_Instruction_List().remove(instruction);
                        // i --;
                        //将这一条指令的左值记录在commonSubExp中
                        common_Sub_Exp.put(resRegister,temp);
                    }else{
                        common_Sub_Phi.put(ops,(Phi_Instruction) instruction);
                    }
                }
                else if (instruction instanceof Zext_Instruction) {
                    List<IR_Value> operands = instruction.get_op();
                    IR_Value resRegister = operands.get(0);
                    operands = operands.subList(1, operands.size());
                    if (common_Sub_Zext.containsKey(operands)) {
                        //如果计算的类型以及操作数相同
                        IR_Value temp = common_Sub_Zext.get(operands).get_op().get(0);
                        //将这一条指令删除
                        instruction.get_bb().get_Instruction_List().remove(instruction);
                        i--;
                        //将这一条指令的左值记录在commonSubExp中
                        common_Sub_Exp.put(resRegister, temp);
                    } else {
                        common_Sub_Zext.put(operands, (Zext_Instruction) instruction);
                    }
                }
                else if (instruction instanceof Type_Trans_Instruction) {
                    List<IR_Value> operands = instruction.get_op();
                    IR_Value resRegister = operands.get(0);
                    operands = operands.subList(1, operands.size());
                    if (common_SubType_Transfer.containsKey(operands) && common_SubType_Transfer.get(operands).get_Trans_Type() == ((Type_Trans_Instruction) instruction).get_Trans_Type()){
                        //如果计算的类型以及操作数相同
                        IR_Value temp = common_SubType_Transfer.get(operands).get_op().get(0);
                        //将这一条指令删除
                        instruction.get_bb().get_Instruction_List().remove(instruction);
                        i--;
                        //将这一条指令的左值记录在commonSubExp中
                        common_Sub_Exp.put(resRegister, temp);
                    } else {
                        common_SubType_Transfer.put(operands, (Type_Trans_Instruction) instruction);
                    }
                }
            }
        }
    }

}
